//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2006
//
// Type sets localization utility.
//

# include "StdAfx.h"
# include "TypeLocalization.h"
# include "ParseHelpers.h"
# include "Log.h"
# include "RgnKind.h"
# include "Types.h"
# include "GarminTypes.h"
# include "Wrappers.h"
# include "Globals.h"

bool g_bTypeLocalization = false;
string_t g_strTypeLocalizationName;

///////////////////////////////////////////////

static
void _LoadDatFile (const char * _strFileName) {
	const CFileForRead hFile (_strFileName);
	if (! hFile.IsValid ())
		return;

	const size_t cSize = ::GetFileSize (hFile, NULL);
	BYTE * const pData = new BYTE [cSize];
	CByteArrayHolder bah (pData);

	DWORD dwRead = 0;
	::ReadFile (hFile, pData, cSize, & dwRead, NULL);
	if (dwRead < cSize)
		return;

	const char * const pStart = reinterpret_cast<const char *> (pData);
	const char * const pEnd   = pStart + cSize;

	if (! HasPrefix (pStart, pEnd, ".IMGTYPES", 9))
		return;

	CTypeSet * pTypeSet      = & g_tsNull;
	CTypeSet * pTypeSetBasic = & g_tsNull;

	bool bLanguageIsDefined = false;

	RGN_KIND Kind = rkMax;
	const char * pLine = pStart;
	do {
		const char * const pNextLine = FindNextLine (pLine, pEnd);
		const size_t cLineLen = GetLineLen (pLine, pNextLine);

		if (cLineLen > 0 && pLine [0] != ';') {
			if (pLine [0] == '.') {
				if (HasPrefix (pLine, pNextLine, ".IMGTYPES", 9)) {
					;
				} else if (HasPrefix (pLine, pNextLine, ".TYPESET", 8)) {
					const char * p = pLine + 8;
					OmitSpaces (& p, pNextLine);
					const size_t cLen = GetLineLen (p, pNextLine);
					pTypeSet = const_cast<CTypeSet *> (CTypeSet::FindTypeSet (string_t (p, cLen).c_str ()));
					if (pTypeSet == NULL || pTypeSet == & g_tsNull) {
						ReportWarning ("Unexpected value of .TYPESET in %s", _strFileName);
						return;
					}
				} else if (HasPrefix (pLine, pNextLine, ".EXTENDS", 8)) {
					const char * p = pLine + 8;
					OmitSpaces (& p, pNextLine);
					const size_t cLen = GetLineLen (p, pNextLine);
					pTypeSetBasic = const_cast<CTypeSet *> (CTypeSet::FindTypeSet (string_t (p, cLen).c_str ()));
					if (pTypeSetBasic == NULL || pTypeSetBasic == & g_tsNull) {
						ReportWarning ("Unexpected value of .EXTENDS in %s", _strFileName);
						return;
					}
				} else if (HasPrefix (pLine, pNextLine, ".LANGID", 7)) {
					if (bLanguageIsDefined)
						ReportWarning ("Duplicated .LANGID in %s", _strFileName);

					// Omit leading spaces.
					const char * p = pLine + 7;
					while (p < pNextLine && (* p == ' ' || * p == '\t'))
						++ p;

					unsigned long ul = 0;
					if (! ParseDecU (p, pNextLine, & p, ul)) {
						ReportWarning ("Can't parse value of .LANGID in %s", _strFileName);
						return;
					}
					const WORD wLangId = ul;

					LANGID langidUser = ::GetUserDefaultLangID ();
					if (PRIMARYLANGID (wLangId) != PRIMARYLANGID (langidUser))
						return;

					bLanguageIsDefined = true;

					char strLanguage [32];
					::GetLocaleInfo (MAKELCID (wLangId, 0), LOCALE_SNATIVELANGNAME, strLanguage, sizeof (strLanguage));
					g_strTypeLocalizationName.assign (strLanguage);
				} else if (HasPrefix (pLine, pNextLine, ".UNKNOWN", 8)) {
					// Omit leading spaces.
					const char * p = pLine + 8;
					while (p < pNextLine && (* p == ' ' || * p == '\t'))
						++ p;

					// Get localized text for 'Unknown'.
					string_t strUnknownLocalized (p, GetLineLen (p, pNextLine));

					assert (pTypeSet);
					pTypeSet->pTypes [0].strTypeNameLocalized = new char [sizeof (char)*(strUnknownLocalized.size () + 1)];
					::strcpy (pTypeSet->pTypes [0].strTypeNameLocalized, strUnknownLocalized.c_str ());
				} else if (HasPrefix (pLine, pNextLine, ".POINT", 6))
					Kind = rkPoint;
				else if (HasPrefix (pLine, pNextLine, ".POLYLINE", 9))
					Kind = rkPolyline;
				else if (HasPrefix (pLine, pNextLine, ".POLYGON", 8))
					Kind = rkPolygon;
				else
					ReportWarning ("Unknown definition in %s", _strFileName);
			} else {
				if (pTypeSet == NULL || pTypeSet == & g_tsNull) {
					ReportWarning ("Missed '.TYPESET' keyword in %s", _strFileName);
					return;
				}
				if (! bLanguageIsDefined) {
					ReportWarning ("Missed '.LANGID' keyword in %s", _strFileName);
					return;
				}

				//
				// Parse substitution pair.
				//

				// Omit leading spaces.
				const char * p = pLine;
				while (p < pNextLine && (* p == ' ' || * p == '\t'))
					++ p;

				// Extract type code.
				unsigned long ul = 0;
				if (p + 2 < pNextLine && p [0] == '0' && p [1] == 'x')
					ParseHex (p + 2, pNextLine, & p, ul);
				else
					ParseDecU (p, pNextLine, & p, ul);

				assert (pTypeSet);
				type_desc_t * const pTD = const_cast<type_desc_t *> (pTypeSet->GetTypeDesc (ul, Kind));
				assert (pTD);
				if (! pTD || pTD->bUnknown) {
					assert (Kind < rkMax);
					ReportWarning ("Unknown type %s 0x%X (%d) in %s.", g_strKinds [Kind], ul, ul, _strFileName);
				} else {
					if (pTD->strTypeNameLocalized)
						ReportWarning ("Duplicated type %s 0x%X (%d) in %s.", g_strKinds [Kind], ul, ul, _strFileName);

					// Omit spaces.
					while (p < pNextLine && (* p == ' ' || * p == '\t'))
						++ p;

					string_t strTypeNameLocalized (p, GetLineLen (p, pNextLine));

					pTD->strTypeNameLocalized = new char [sizeof (char)*(strTypeNameLocalized.size () + 1)];
					::strcpy (pTD->strTypeNameLocalized, strTypeNameLocalized.c_str ());
					pTypeSet->bHasLocalizedTypes = true;
				}
			}
		}

		pLine = pNextLine;
	} while (pLine < pEnd);

	if (pTypeSet && pTypeSetBasic != & g_tsNull)
		pTypeSet->pBasicTypeSet = pTypeSetBasic;

	g_bTypeLocalization = true;
}

static
void _CompleteGarminMarinePOIs () {
	for (size_t cType = 0; cType < g_tsGarmin.cTypes; ++ cType) {
		type_desc_t & td = g_tsGarmin.pTypes [cType];
		if (td.bUnknown || td.Kind != rkPoint)
			continue;
		if (td.strTypeNameLocalized != NULL)
			continue;

		const BYTE btTypeHi = td.ushType >> 8;
		if (0x17 > btTypeHi && btTypeHi > 0x1b)
			continue;
		const BYTE btTypeLo = td.ushType & 0xFF;

		const type_desc_t * const pTDBasic = g_tsGarmin.GetTypeDesc (0x1600 | btTypeLo, td.Kind);
		if (pTDBasic != NULL && pTDBasic->strTypeNameLocalized) {
			td.strTypeNameLocalized = new char [sizeof (char)*(::strlen (pTDBasic->strTypeNameLocalized) + 1)];
			::strcpy (td.strTypeNameLocalized, pTDBasic->strTypeNameLocalized);
		}
	}
}

static
void _CompleteBasicTypeSetLocalization (CTypeSet * _pTypeSet) {
	assert (_pTypeSet);

	if (_pTypeSet->bLocalizationIsReady)
		return;
	_pTypeSet->bLocalizationIsReady = true;

	CTypeSet * const pTypeSetBasic = _pTypeSet->pBasicTypeSet;
	if (pTypeSetBasic == NULL || pTypeSetBasic == & g_tsNull)
		return;

	if (! pTypeSetBasic->bLocalizationIsReady)
		_CompleteBasicTypeSetLocalization (pTypeSetBasic);

	// Perform automatic copy of descriptions.
	for (size_t cType = 0; cType < _pTypeSet->cTypes; ++ cType) {
		type_desc_t & td = _pTypeSet->pTypes [cType];
		if (td.bUnknown)
			continue;
		if (td.strTypeNameLocalized != NULL)
			continue;

		// Copy descriptions from basic type set.
		const type_desc_t * const pTDBasic = pTypeSetBasic->GetTypeDesc (td.ushType, td.Kind);
		if (pTDBasic != NULL && pTDBasic->strTypeNameLocalized) {
			td.strTypeNameLocalized = new char [sizeof (char)*(::strlen (pTDBasic->strTypeNameLocalized) + 1)];
			::strcpy (td.strTypeNameLocalized, pTDBasic->strTypeNameLocalized);
			_pTypeSet->bHasLocalizedTypes = true;
		}
	}
}

///////////////////////////////////////////

void LoadTypeSetLocalization () {
	// Get folder with mapedit.exe.
	char strProcessName [MAX_PATH + 1];
	::GetModuleFileName (NULL, strProcessName, MAX_PATH);
	const char * const pPathEnd = ::strrchr (strProcessName, '\\');

	string_t strFilter;
	if (pPathEnd)
		strFilter.assign ((const char *) strProcessName, pPathEnd + 1);
	strFilter.append ("*.dat");

	WIN32_FIND_DATA fd;
	const HANDLE hFind = ::FindFirstFile (strFilter.c_str (), & fd);
	if (hFind == INVALID_HANDLE_VALUE)
		return;

	do {
		if (pPathEnd)
			strFilter.assign ((const char *) strProcessName, pPathEnd + 1);
		strFilter.append (fd.cFileName);

		_LoadDatFile (strFilter.c_str ());
	} while (::FindNextFile (hFind, & fd));

	::FindClose (hFind);

	_CompleteGarminMarinePOIs ();
	for (CTypeSet * pTypeSet = CTypeSet::s_pFirst; pTypeSet; pTypeSet = pTypeSet->m_pNext)
		_CompleteBasicTypeSetLocalization (pTypeSet);

	if (! IsLogEmpty ())
		ShowMessageLog ();
}
